home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
b
/
b.lha
/
B
/
src
/
bed
/
supr.c
< prev
next >
Wrap
C/C++ Source or Header
|
1988-11-24
|
18KB
|
1,118 lines
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1984. */
static char rcsid[] = "$Header: supr.c,v 2.3 84/07/23 13:03:15 guido Exp $";
/*
* B editor -- Superroutines.
*/
#include "b.h"
#include "feat.h"
#include "bobj.h"
#include "node.h"
#include "supr.h"
#include "gram.h"
/*
* Compute the length of the ep->s1'th item of node tree(ep->focus).
*/
Visible int
lenitem(ep)
register environ *ep;
{
register node n = tree(ep->focus);
register node nn;
if (ep->s1&1) /* Fixed text */
return fwidth(noderepr(n)[ep->s1/2]);
/* Else, variable text or a whole node */
nn = child(n, ep->s1/2);
return width(nn);
}
/*
* Find the largest possible representation of the focus.
* E.g., a WHOLE can also be represented as a SUBSET of its parent,
* provided it has a parent.
* Also, a SUBSET may be extended with some empty left and right
* items and then look like a WHOLE, etc.
* This process is repeated until no more improvements can be made.
*/
Visible Procedure
grow(ep)
environ *ep;
{
subgrow(ep, Yes);
}
Visible Procedure
subgrow(ep, ignorespaces)
register environ *ep;
bool ignorespaces;
{
register node n;
register int sym;
register int i;
register int len;
register string repr;
switch (ep->mode) {
case ATBEGIN:
case ATEND:
case VHOLE:
case FHOLE:
ritevhole(ep);
if (ep->mode != FHOLE && ep->mode != VHOLE || lenitem(ep) == 0)
leftvhole(ep);
}
for (;;) {
n = tree(ep->focus);
sym = symbol(n);
switch (ep->mode) {
case VHOLE:
case FHOLE:
if ((sym == Optional || sym == Hole) && ep->s2 == 0) {
ep->mode = WHOLE;
continue;
}
if (lenitem(ep) <= 0) {
ep->mode = SUBSET;
ep->s2 = ep->s1;
continue;
}
return;
case ATBEGIN:
case ATEND:
if (sym == Optional || sym == Hole) {
ep->mode = WHOLE;
continue;
}
return;
case SUBRANGE:
if (ep->s1&1) {
repr = noderepr(n)[ep->s1/2];
len = fwidth(repr);
if (!ignorespaces) {
while (ep->s2 > 0 && repr[ep->s2-1] == ' ')
--ep->s2;
while (ep->s3 < len && repr[ep->s3+1] == ' ')
++ep->s3;
}
}
else
len = Length((value) firstchild(n));
if (ep->s2 == 0 && ep->s3 >= len - 1) {
ep->mode = SUBSET;
ep->s2 = ep->s1;
continue;
}
return;
case SUBSET:
subgrsubset(ep, ignorespaces);
if (ep->s1 == 1) {
if (ep->s2 == 2*nchildren(n) + 1) {
ep->mode = WHOLE;
continue;
}
if (ep->s2 == 2*nchildren(n) - 1 && issublist(sym)) {
ep->mode = SUBLIST;
ep->s3 = 1;
return;
}
}
return;
case SUBLIST:
for (i = ep->s3; i > 0; --i)
n = lastchild(n);
sym = symbol(n);
if (sym == Optional) {
ep->mode = WHOLE;
continue;
}
return;
case WHOLE:
ep->s1 = 2*ichild(ep->focus);
if (up(&ep->focus)) {
ep->mode = SUBSET;
ep->s2 = ep->s1;
higher(ep);
continue;
}
return; /* Leave as WHOLE if there is no parent */
default:
Abort();
/* NOTREACHED */
}
}
/* Not reached */
}
/*
* Ditto to find smallest possible representation.
*/
Visible Procedure
shrink(ep)
register environ *ep;
{
register node n;
register int sym;
for (;;) {
n = tree(ep->focus);
sym = symbol(n);
switch (ep->mode) {
case WHOLE:
if (sym == Hole || sym == Optional)
return;
ep->mode = SUBSET;
ep->s1 = 1;
ep->s2 = 2*nchildren(n) + 1;
continue;
case SUBLIST:
if (sym == Hole || sym == Optional) {
ep->mode = WHOLE;
return;
}
if (ep->s3 == 1) {
ep->mode = SUBSET;
ep->s1 = 1;
ep->s2 = 2*nchildren(n) - 1;
continue;
}
return;
case SUBSET:
if (sym == Hole || sym == Optional) {
ep->mode = WHOLE;
return;
}
shrsubset(ep);
if (ep->s1 == ep->s2) {
if (isunititem(ep)) {
ep->mode = SUBRANGE;
ep->s2 = 0;
ep->s3 = lenitem(ep) - 1;
return;
}
else {
s_downi(ep, ep->s1/2);
ep->mode = WHOLE;
continue;
}
}
return;
case SUBRANGE:
if (sym == Optional || sym == Hole)
ep->mode = WHOLE;
return;
case ATBEGIN:
ritevhole(ep);
if (ep->mode == ATBEGIN) {
if (sym == Optional || sym == Hole)
ep->mode = WHOLE;
return;
}
continue;
case FHOLE:
case VHOLE:
ritevhole(ep);
if (ep->mode != VHOLE && ep->mode != FHOLE)
continue;
sym = symbol(tree(ep->focus));
if (sym == Optional || sym == Hole && ep->s2 == 0)
ep->mode = WHOLE;
return;
case ATEND:
return;
default:
Abort();
/* NOTREACHED */
}
}
/* Not reached */
}
/*
* Subroutine to find the largest way to describe a SUBSET focus
* (modulo surrounding blanks and newlines).
*/
Visible Procedure
growsubset(ep)
environ *ep;
{
subgrsubset(ep, Yes);
}
Visible Procedure
subgrsubset(ep, ignorespaces)
register environ *ep;
bool ignorespaces;
{
register node n = tree(ep->focus);
register string *rp = noderepr(n);
register nch21 = nchildren(n)*2 + 1;
register int i;
Assert(ep->mode == SUBSET);
for (i = ep->s1; i > 1 && subisnull(n, rp, i-1, ignorespaces); --i)
;
ep->s1 = i;
for (i = ep->s2; i < nch21 && subisnull(n, rp, i+1, ignorespaces); ++i)
;
ep->s2 = i;
}
/*
* Ditto for the smallest way.
*/
Visible Procedure /* Ought to be Hidden */
shrsubset(ep)
register environ *ep;
{
register node n = tree(ep->focus);
register string *rp = noderepr(n);
register int s1 = ep->s1;
register int s2 = ep->s2;
for (; s1 < s2 && isnull(n, rp, s1); ++s1)
;
ep->s1 = s1;
for (; s2 > s1 && isnull(n, rp, s2); --s2)
;
ep->s2 = s2;
}
/*
* Subroutine for grow/shrink to see whether item i is (almost) invisible.
*/
Visible bool
isnull(n, rp, i)
node n;
string *rp;
int i;
{
return subisnull(n, rp, i, Yes);
}
Hidden Procedure
subisnull(n, rp, i, ignorespaces)
register node n;
register string *rp;
register int i;
bool ignorespaces;
{
register string repr;
register node nn;
if (i&1) { /* Fixed text */
repr = rp[i/2];
return !Fw_positive(repr) || ignorespaces && allspaces(repr);
}
nn = child(n, i/2);
return width(nn) == 0;
}
/*
* Find the rightmost VHOLE which would look the same as the current one.
*/
Visible Procedure
ritevhole(ep)
register environ *ep;
{
register node n;
register int ich;
register int len;
register int s1save;
for (;;) {
n = tree(ep->focus);
switch (ep->mode) {
case WHOLE:
ep->mode = ATEND;
break;
case VHOLE:
case FHOLE:
len = lenitem(ep);
Assert(len >= 0);
if (ep->s2 < len)
return; /* Hole in middle of string */
s1save = ep->s1;
if (nextitem(ep)) {
if (isunititem(ep)) {
ep->mode = (ep->s1&1) ? FHOLE : VHOLE;
ep->s2 = 0;
}
else if (fwidth(noderepr(child(n, ep->s1/2))[0]) < 0) {
/* Next item begins with newline -- avoid */
ep->s1 = s1save;
return;
}
else {
s_downi(ep, ep->s1/2);
ep->mode = ATBEGIN;
}
break;
}
ep->mode = ATEND;
/* Fall through */
case ATEND:
if (!parent(ep->focus) || width(n) < 0)
return;
ich = ichild(ep->focus);
ep->s1 = 2*ich;
s_up(ep);
if (nextitem(ep)) {
/* Note -- negative width cannot occur (see test above) */
if (isunititem(ep)) {
ep->mode = (ep->s1&1) ? FHOLE : VHOLE;
ep->s2 = 0;
}
else {
ep->mode = ATBEGIN;
s_downi(ep, ep->s1/2);
}
break;
}
continue;
case ATBEGIN:
if (fwidth(noderepr(n)[0]) < 0)
return; /* Already at dangerous position */
ep->mode = FHOLE;
ep->s1 = 1;
ep->s2 = 0;
continue;
default:
Abort();
/* NOTREACHED */
}
}
}
/*
* Ditto to the left.
*/
Visible Procedure
leftvhole(ep)
register environ *ep;
{
register int ich;
for (;;) {
switch (ep->mode) {
case WHOLE:
ep->mode = ATBEGIN;
break;
case VHOLE:
case FHOLE:
if (ep->s2 > 0)
return;
if (previtem(ep)) {
if (isunititem(ep)) {
ep->mode = (ep->s1&1) ? FHOLE : VHOLE;
ep->s2 = lenitem(ep);
}
else {
s_downi(ep, ep->s1/2);
ep->mode = ATEND;
}
}
else if (fwidth(noderepr(tree(ep->focus))[0]) < 0)
return;
else
ep->mode = ATBEGIN;
continue;
case ATBEGIN:
ich = ichild(ep->focus);
if (!up(&ep->focus))
return;
higher(ep);
ep->s1 = 2*ich;
if (prevnnitem(ep)) {
if (isunititem(ep)) {
ep->mode = (ep->s1&1) ? FHOLE : VHOLE;
ep->s2 = lenitem(ep);
}
else {
s_downi(ep, ep->s1/2);
ep->mode = ATEND;
}
}
else if (fwidth(noderepr(tree(ep->focus))[0]) < 0) {
s_downi(ep, ich); /* Undo up */
return;
}
else
ep->mode = ATBEGIN;
continue;
case ATEND:
lastnnitem(ep);
if (isunititem(ep)) {
ep->s2 = lenitem(ep);
ep->mode = (ep->s1&1) ? FHOLE : VHOLE;
}
else
s_downi(ep, ep->s1/2);
continue;
default:
Abort();
}
}
}
/*
* Safe up, downi, left and rite routines:
* 1) Rather die than fail;
* 2) Update ep->highest properly.
*/
Visible Procedure
s_up(ep)
register environ *ep;
{
if (!up(&ep->focus))
syserr("s_up failed");
higher(ep);
}
Visible Procedure
s_downi(ep, i)
register environ *ep;
register int i;
{
if (!downi(&ep->focus, i))
syserr("s_downi failed");
}
Visible Procedure
s_down(ep)
register environ *ep;
{
if (!down(&ep->focus))
syserr("s_down failed");
}
Visible Procedure
s_downrite(ep)
register environ *ep;
{
if (!downrite(&ep->focus))
syserr("s_downrite failed");
}
Visible Procedure
s_left(ep)
register environ *ep;
{
register int ich = ichild(ep->focus);
s_up(ep);
s_downi(ep, ich-1);
}
Visible Procedure
s_rite(ep)
register environ *ep;
{
register int ich = ichild(ep->focus);
s_up(ep);
s_downi(ep, ich+1);
}
/*
* Find next item in a subset, using ep->s1 as index.
* (This used to be less trivial, so it's still a subroutine rather than
* coded in-line or as a macro.)
*/
Visible bool
nextitem(ep)
register environ *ep;
{
if (ep->s1 >= 2*nchildren(tree(ep->focus)) + 1)
return No; /* Already at last item */
++ep->s1;
return Yes;
}
/*
* Ditto for previous.
*/
Visible bool
previtem(ep)
register environ *ep;
{
if (ep->s1 <= 1
|| ep->s1 == 2 && fwidth(noderepr(tree(ep->focus))[0]) < 0)
return No; /* Already at first item */
--ep->s1;
return Yes;
}
/*
* Test whether item ep->s1 is "small", i.e., fixed or varying text
* but not a whole subtree.
*/
Visible bool
isunititem(ep)
register environ *ep;
{
return (ep->s1&1) || Type(child(tree(ep->focus), ep->s1/2)) == Tex;
}
/*
* Check for consistent mode information.
*/
Visible bool
checkep(ep)
register environ *ep;
{
switch (ep->mode) {
case FHOLE:
if (!(ep->s1&1))
break;
if (ep->s2 < 0 || ep->s2 > lenitem(ep))
break;
return Yes;
case VHOLE:
if (!(ep->s1&1)) {
if (Type(child(tree(ep->focus), ep->s1/2)) != Tex)
break;
}
if (ep->s2 < 0 || ep->s2 > lenitem(ep))
break;
return Yes;
case SUBSET:
if (ep->s2 == ep->s1 && isunititem(ep) && lenitem(ep) <= 0)
break;
return Yes;
default:
return Yes;
}
dbmess(ep);
return No;
}
/*
* Like {next,prev,first,last}item, but with empty items skipped
* (i.e., those with length <= 0).
*/
Visible bool
nextnnitem(ep)
register environ *ep;
{
register int s1save = ep->s1;
while (nextitem(ep)) {
if (lenitem(ep) != 0)
return Yes;
}
ep->s1 = s1save;
return No;
}
Visible bool
prevnnitem(ep)
register environ *ep;
{
register int s1save = ep->s1;
register int len;
while (previtem(ep)) {
len = lenitem(ep);
if (len > 0 || len < 0 && ep->s1 > 1)
return Yes;
}
ep->s1 = s1save;
return No;
}
Visible Procedure
firstnnitem(ep)
register environ *ep;
{
ep->s1 = fwidth(noderepr(tree(ep->focus))[0]) < 0 ? 2 : 1;
while (lenitem(ep) == 0) {
if (!nextitem(ep))
break;
}
return;
}
Visible Procedure
lastnnitem(ep)
register environ *ep;
{
ep->s1 = 2*nchildren(tree(ep->focus)) + 1;
while (lenitem(ep) == 0) {
if (!previtem(ep))
break;
}
return;
}
/*
* Prepare the focus for insertion.
* If the focus isn't a hole, make a hole just before it which becomes the
* new focus.
* Also repair strange statuses left by moves, so we may have more chance
* to insert a character.
*/
Visible Procedure
fixit(ep)
register environ *ep;
{
/* First, make a hole if it's not already a hole. */
switch (ep->mode) {
case FHOLE:
break;
case VHOLE:
if (ep->s1&1)
ep->mode = FHOLE;
break;
case SUBRANGE:
if (ep->s1&1)
ep->mode = FHOLE;
else
ep->mode = VHOLE;
break;
case SUBSET:
if (ep->s1&1) {
if (ep->s1 == 1)
ep->mode = ATBEGIN;
else {
ep->mode = FHOLE;
ep->s2 = 0;
}
}
else if (Type(child(tree(ep->focus), ep->s1/2)) == Tex) {
ep->mode = VHOLE;
ep->s2 = 0;
}
else {
s_downi(ep, ep->s1/2);
ep->mode = ATBEGIN;
}
break;
case ATBEGIN:
case SUBLIST:
case WHOLE:
ep->mode = ATBEGIN;
break;
case ATEND:
break;
default:
Abort();
}
leftvhole(ep);
if (ep->mode == ATEND && symbol(tree(ep->focus)) == Hole)
ep->mode = WHOLE; /***** Experiment! *****/
}
/*
* Small utility to see if a string contains only spaces
* (this is true for the empty string "").
* The string pointer must not be null!
*/
Visible bool
allspaces(str)
register string str;
{
Assert(str);
for (; *str; ++str) {
if (*str != ' ')
return No;
}
return Yes;
}
/*
* Function to compute the actual width of the focus.
*/
Visible int
focwidth(ep)
register environ *ep;
{
node nn;
register node n = tree(ep->focus);
register string *rp = noderepr(n);
register int i;
register int w;
int len = 0;
switch (ep->mode) {
case VHOLE:
case FHOLE:
case ATEND:
case ATBEGIN:
return 0;
case WHOLE:
return width(n);
case SUBRANGE:
return ep->s3 - ep->s2 + 1;
case SUBSET:
for (i = ep->s1; i <= ep->s2; ++i) {
if (i&1)
w = fwidth(rp[i/2]);
else {
nn = child(n, i/2);
w = width(nn);
}
if (w < 0 && len >= 0)
len = w;
else if (w >= 0 && len < 0)
;
else
len += w;
}
return len;
case SUBLIST:
len = width(n);
for (i = ep->s3; i > 0; --i)
n = lastchild(n);
w = width(n);
if (w < 0 && len >= 0)
return w;
if (w >= 0 && len < 0)
return len;
return len - w;
default:
Abort();
/* NOTREACHED */
}
}
/*
* Compute the offset of the focus from the beginning of the current node.
* This may be input again to fixfocus to allow restoration of this position.
*/
Visible int
focoffset(ep)
register environ *ep;
{
node nn;
register node n;
register string *rp;
register int w;
register int len;
register int i;
switch (ep->mode) {
case WHOLE:
case SUBLIST:
return 0;
case ATBEGIN:
return ep->spflag;
case ATEND:
w = width(tree(ep->focus));
if (w < 0)
return w;
return w + ep->spflag;
case SUBSET:
case FHOLE:
case VHOLE:
case SUBRANGE:
n = tree(ep->focus);
rp = noderepr(n);
len = 0;
for (i = 1; i < ep->s1; ++i) {
if (i&1)
w = Fwidth(rp[i/2]);
else {
nn = child(n, i/2);
w = width(nn);
}
if (w < 0) {
if (len >= 0)
len = w;
else
len += w;
}
else if (len >= 0)
len += w;
}
if (ep->mode == SUBSET || len < 0)
return len;
return len + ep->s2 + ep->spflag;
default:
Abort();
/* NOTREACHED */
}
}
/*
* Return the first character of the focus (maybe '\n'; 0 if zero-width).
*/
Visible int
focchar(ep)
environ *ep;
{
node n = tree(ep->focus);
string str;
string *rp;
int i;
int c;
switch (ep->mode) {
case VHOLE:
case FHOLE:
case ATBEGIN:
case ATEND:
return 0;
case WHOLE:
case SUBLIST:
return nodechar(n);
case SUBSET:
rp = noderepr(n);
for (i = ep->s1; i <= ep->s2; ++i) {
if (i&1) {
if (!Fw_zero(rp[i/2]))
return rp[i/2][0];
}
else {
c = nodechar(child(n, i/2));
if (c)
return c;
}
}
return 0;
case SUBRANGE:
if (ep->s1&1)
str = noderepr(n)[ep->s1/2];
else {
Assert(Type(child(n, ep->s1/2)) == Tex);
str = Str((value)child(n, ep->s1/2));
}
return str[ep->s2];
default:
Abort();
/* NOTREACHED */
}
}
/*
* Subroutine to return first character of node.
*/
Visible int
nodechar(n)
node n;
{
string *rp;
int nch;
int i;
int c;
if (Type(n) == Tex)
return Str((value)n)[0];
rp = noderepr(n);
if (!Fw_zero(rp[0]))
return rp[0][0];
nch = nchildren(n);
for (i = 1; i <= nch; ++i) {
c = nodechar(child(n, i));
if (c)
return c;
if (!Fw_zero(rp[i]))
return rp[i][0];
}
return 0;
}
/*
* Function to compute the actual indentation level at the focus.
*/
Visible int
focindent(ep)
environ *ep;
{
int y = Ycoord(ep->focus);
int x = Xcoord(ep->focus);
int level = Level(ep->focus);
node n = tree(ep->focus);
switch (ep->mode) {
case WHOLE:
case ATBEGIN:
case SUBLIST:
break;
case ATEND:
evalcoord(n, 1 + nchildren(n), &y, &x, &level);
break;
case SUBSET:
case FHOLE:
case VHOLE:
evalcoord(n, ep->s1/2, &y, &x, &level);
break;
default:
Abort();
}
return level;
}
/*
* Routines to move 'environ' structures.
*/
emove(s, d)
environ *s;
environ *d;
{
#ifdef STRUCTASS
*d = *s;
#else !STRUCTASS
d->focus = s->focus;
d->mode = s->mode;
d->copyflag = s->copyflag;
d->spflag = s->spflag;
d->changed = s->changed;
d->s1 = s->s1;
d->s2 = s->s2;
d->s3 = s->s3;
d->highest = s->highest;
d->copybuffer = s->copybuffer;
#ifdef RECORDING
d->oldmacro = s->oldmacro;
d->newmacro = s->newmacro;
#endif RECORDING
d->generation = s->generation;
#endif !STRUCTASS
}
ecopy(s, d)
environ *s;
environ *d;
{
emove(s, d);
pathcopy(d->focus);
copy(d->copybuffer);
#ifdef RECORDING
copy(d->oldmacro);
copy(d->newmacro);
#endif RECORDING
}
erelease(e)
environ *e;
{
pathrelease(e->focus);
release(e->copybuffer);
#ifdef RECORDING
release(e->oldmacro);
release(e->newmacro);
#endif RECORDING
}